/*
 * Copyright (c) 2017-present, CV4J Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cv4j.rxcv4j;

import ohos.agp.components.Image;
import ohos.agp.window.dialog.IDialog;
import ohos.media.image.PixelMap;

import com.cv4j.core.datamodel.CV4JImage;
import com.cv4j.core.datamodel.ImageProcessor;
import com.cv4j.core.filters.CommonFilter;
import com.cv4j.rxcv4j.util.RxJavaUtils;
import com.cv4j.uitl.Log;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.functions.Function;

/**
 * rx图像数据
 *
 * @since 2021-05-17
 */
public class RxImageData {
    WrappedCV4JImage wrappedCV4JImage;
    List<CommonFilter> filters;
    MemCache memCache;
    Single<WrappedCV4JImage> single;
    boolean useCache = true;
    Image imageView;
    IDialog mDialog = null;

    /**
     * rx图像数据
     *
     * @param image 图像
     */
    public RxImageData(CV4JImage image) {
        filters = new ArrayList<>();
        memCache = MemCache.INSTANCE;
        wrappedCV4JImage = new WrappedCV4JImage(image, filters);
        single = Single.just(wrappedCV4JImage);
    }

    /**
     * rx图像数据
     *
     * @param bytes 字节
     */
    public RxImageData(byte[] bytes) {
        this(new CV4JImage(bytes));
    }

    /**
     * rx图像数据
     *
     * @param bitmap 位图
     */
    public RxImageData(PixelMap bitmap) {
        this(new CV4JImage(bitmap));
    }

    /**
     * 对话框
     *
     * @param dialog 对话框
     * @return {@link RxImageData}
     */
    public RxImageData dialog(IDialog dialog) {
        if (dialog == null) {
            return this;
        }
        this.mDialog = dialog;
        this.mDialog.show();
        return this;
    }

    /**
     * 添加过滤器
     * 使用滤镜，支持链式调用多个滤镜
     *
     * @param filter *
     * @return RxImageData
     */
    public RxImageData addFilter(CommonFilter filter) {
        if (filter == null) {
            Log.error("RxImageData", "filter is null");
            return this;
        }

        filters.add(filter);
        return this;
    }

    /**
     * 使用缓存
     * 判断是否使用缓存，默认情况下是使用缓存。
     * 该方法需要在into()方法之前使用。
     *
     * @param useCache *
     * @return RxImageData
     */
    public RxImageData isUseCache(boolean useCache) {
        this.useCache = useCache;
        return this;
    }

    /**
     * 成
     * RxImageData.bitmap(bitmap).addFilter(new ColorFilter()).into(view);
     *
     * @param image 图像
     */
    public void into(Image image) {
        if (image == null) {
            Log.error("RxImageData", "imageview is null");
            return;
        }
        this.imageView = image;
        render();
    }

    /**
     * 渲染
     * 渲染imageview
     */
    private void render() {
        if (imageView == null) {
            return;
        }
        if (filters.size() == 0) {
            this.single.compose(RxJavaUtils.singleToMain())
                .doFinally(() -> dismissDiaog())
                .subscribe(wrappedCV4JImage ->
                        imageView.setPixelMap(wrappedCV4JImage.image.toBitmap()),
                    throwable -> {
                        Log.error("RxImageData", throwable.toString());
                    });
        } else if (filters.size() == 1) {
            this.single.map((Function<WrappedCV4JImage, ImageProcessor>) wrappedCV4JImage -> {
                if (useCache) {
                    StringBuilder sb = new StringBuilder();
                    if (imageView.getContext() != null) {
                        sb.append(imageView.getContext().getClass().getSimpleName());
                    }
                    sb.append(filters.get(0).getClass().getSimpleName()).append(imageView.getId());

                    // 目前key采用ability name + filter name + imageView id
                    String key = Utils.getSHA256StrJava(sb.toString());
                    Optional<PixelMap> pixelMap = memCache.get(key);
                    if (pixelMap == null || !pixelMap.isPresent()) {
                        Optional<ImageProcessor> imageProcessor =
                            filters.get(0).filter(wrappedCV4JImage.getImage().getProcessor());
                        if (imageProcessor.isPresent()){
                            memCache.put(key, imageProcessor.get().getImage().toBitmap());
                        }
                        return imageProcessor.get();
                    } else {
                        wrappedCV4JImage.getImage().getProcessor().getImage().setBitmap(memCache.get(key).get());
                        return wrappedCV4JImage.getImage().getProcessor();
                    }
                } else {
                    return filters.get(0).filter(wrappedCV4JImage.getImage().getProcessor()).get();
                }
            }).compose(RxJavaUtils.singleToMain())
                .doFinally(() -> dismissDiaog())
                .subscribe(wrappedCV4JImage ->
                        imageView.setPixelMap(wrappedCV4JImage.getImage().toBitmap()),
                    throwable -> {
                        Log.error("RxImageData", throwable.toString());
                    });
        } else {
            this.single
                .map(new Function<WrappedCV4JImage, List<CommonFilter>>() {
                    @Override
                    public List<CommonFilter> apply(WrappedCV4JImage wrappedCV4JImage) throws Throwable {
                        return wrappedCV4JImage.filters;
                    }
                }).map(new Function<List<CommonFilter>, ImageProcessor>() {
                @Override
                public ImageProcessor apply(List<CommonFilter> commonFilters) throws Throwable {
                    return filter(wrappedCV4JImage.image.getProcessor());
                }
            }).compose(RxJavaUtils.singleToMain())
                .doFinally(() -> dismissDiaog())
                .subscribe(imageProcessor ->
                        imageView.setPixelMap(imageProcessor.getImage().toBitmap()),
                    throwable -> {
                        Log.error("RxImageData", throwable.toString());
                    });
        }
    }

    /**
     * 过滤器
     *
     * @param imageData 图像数据
     * @return {@link ImageProcessor}
     */
    private ImageProcessor filter(ImageProcessor imageData) {
        if (filters.size() > 0) {
            return filter(imageData, filters.size());
        } else {
            return imageData;
        }
    }

    /**
     * 过滤器
     *
     * @param imageData 图像数据
     * @param size 大小
     * @return {@link ImageProcessor}
     */
    private ImageProcessor filter(ImageProcessor imageData, int size) {
        int s1 = size;
        if (s1 == 1) {
            CommonFilter filter = filters.get(0);
            return filter.filter(imageData).get();
        }
        s1 = size - 1;
        CommonFilter filter = filters.get(s1);
        imageData = filter.filter(imageData).get();
        return filter(imageData, s1);
    }


    /**
     * 驳回diaog
     */
    private void dismissDiaog() {
        if (mDialog != null) {
            mDialog.hide();
            mDialog = null;
        }
    }

    /**
     * 回收
     * 释放资源
     */
    public void recycle() {
        wrappedCV4JImage.getImage().recycle();
    }


    /**
     * 字节
     *
     * @param bytes 字节
     * @return {@link RxImageData}
     */
    public static RxImageData bytes(byte[] bytes) {
        return new RxImageData(bytes);
    }

    /**
     * 位图
     *
     * @param pixelMap 像素映射
     * @return {@link RxImageData}
     */
    public static RxImageData bitmap(PixelMap pixelMap) {
        return new RxImageData(pixelMap);
    }

    /**
     * 图像
     *
     * @param image 图像
     * @return {@link RxImageData}
     */
    public static RxImageData image(CV4JImage image) {
        return new RxImageData(image);
    }
}
